#ifndef TRI_TEXTURE_H_
#define TRI_TEXTURE_H_

#include <iostream>
//Include library header
#include <GL/gl.h>
#include <GL/glu.h>
#include <map>
#include <fstream>

#include "base/exception.h"
#include "base/system.h"
#include "base/utils.h"
#include "base/rectangle.h"

namespace tri {

#define IMAGE_DOES_NOT_EXISTS = -1
#define IMAGE_LOAD_ERROR = -2

#define TRI_RGB GL_RGB
#define TRI_RGBA GL_RGBA

/**
 * Image data structure. Used to load images
 * in opengl.
 */
class TImageData {
public:
	TImageData();
	virtual ~TImageData();
	void clear();
	int width;
	int height;
	int bits;
	int format;
	unsigned char* data;
};

void writeImageDataFile(std::string file,std::vector<std::string>& images);
bool readImageDataFile(std::string data_file,std::string image_file,tri::TImageData* img);
bool readImageDataFileFiles(std::string data_file,std::vector<std::string>& list);

/**
 * Load an image from disk.
 * mut release data with free_image(TImageData*)
 */
bool loadImage(TImageData *im, std::string file);

/**
 * Free image memory
 */
void freeImage(TImageData *im);

/**
 * Texture Exception
 */
class TriTextureOverSizeException {
public:
	TriTextureOverSizeException(const char* msg);
	const char * what();
private:
	const char* message;
};

class TTexture {
public:
	TTexture();
	virtual ~TTexture();

	/**
	 * Load the texture from image data.
	 *
	 * @param Reference to image data structure
	 */
	void load(TImageData *im, bool mipmap=false,bool filter = true);

	/**
	 * Load a blank texture with width and height
	 * specified.
	 *
	 * @param Reference to image data structure
	 */
	void load(int w, int h, int mode = TRI_RGBA);

	/**
	 * Load the texture from file.
	 *
	 * @param Reference to image data structure
	 */
	void load(std::string file, bool mipmap=false, bool filter = true);

	/**
	 * Delete the texture
	 */
	void del();

	/**
	 * Bind this txexture to the current context
	 */
	void bind();

	/**
	 * Update the texture if it was already created.
	 */
	void update(TImageData *im);

	/**
	 * Update the texture if it was already created.
	 */
	void update(std::string file);

	/**
	 * Has the texture alredy been create from an image
	 */
	bool created();

	/**
	 * Get the corrected texture coords for this texture
	 */
	TRectangle getTC();

	/**
	 * Get the texture dimensions
	 */
	void getTextureSize(int &width, int &height);

	
	void addSpriteData(std::string key, TRectangle rect);
	void resetTC();
	void setSprite(std::string key);
	void setSprite(TRectangle rect);
	float getAspectRatio();
	int getWidth();
	int getHeight();
	unsigned int getTexID();
	
	static void setImageDataFile(std::string);
	static std::string getImageDataFile();
	static void addImageDataFile(std::string);
	static std::string getImageFileFromImage(std::string image);
	

private:

	void _gen_id();

	/**
	 * Storage for the texure ID. genrated by OpenGL when
	 * the texture is made
	 */
	unsigned int id;

	/**
	 * Flag indicates if this texture was created
	 * using glut and not gl
	 */
	bool mipmap;

	/**
	 * Texture width
	 */
	int width;

	/**
	 * Texture height
	 */
	int height;

	/**
	 * Actual texture width
	 */
	int raw_width;

	/**
	 * Actual texture height
	 */
	int raw_height;

	/**
	 * texture pixel bits size
	 */
	int bits;

	/**
	 * Pixel format
	 */
	int format;
	
	/**
	 * File name if texture was made from a file
	 */
	std::string mFile;

	TRectangle tc;

	std::map<std::string , TRectangle> sprites;
	
	static std::string mImageDataFile;
	static std::map< std::string , std::vector<std::string> > mImageDataIndex;
};

void applyTexture(std::string _image);

class TTextureCache {

public:
	TTextureCache();
	virtual ~TTextureCache();
	/**
	 * Returns true if the key exists. 
	 */
	bool containes(std::string key);

	/**
	 * Add a new instance of T
	 */
	void add(std::string key);

	/**
	 * Remove the instance of T bound to the 
	 * key.  
	 */
	void remove(std::string key);

	/**
	 * Get a reference to the 
	 */
	TTexture* get(std::string key);

	static TTextureCache cache;

private:
	/**
	 * map the items to a unique string
	 */
	std::map<std::string,TTexture> _map;
};

/**
 * create new texture
 */
void createTexture(std::string key);
void createTexture(std::string key, std::string file,bool mipmap=false,bool filter = true);

/**
 * Remove the instance of texture bound to the 
 * key.  
 */
void deleteTexture(std::string key);

/**
 * Get a reference to the 
 */
//TTexture* _getTexture(std::string key);
TTexture* getTexture(std::string key);

/**
 * Unbind all texturesd from the oengl context
 */
void unbindAllTextures();

}

std::ostream &operator<<( std::ostream&, const tri::TImageData&);
	

#endif
